/* eslint-disable no-redeclare */
import CreateEntity from './classes/CreateEntity';
import FormUtil from './linkedList/FormUtil';
import ItemRowList from './linkedList/ItemRowList';

export type YNodesFunction = (options: {
  form: YForm;
  formData: { [key: string]: any };
  $props: { [key: string]: any };
  scoped: any;
}) => YNodes;

export type YNodes =
  | YNodesFunction
  | (YItemConfig | YItemRow[] | string | YItemRow)[]
  | YItemRow
  | string
  | boolean
  | number
  | YItemConfig
  | { [key: string]: YNodes };
export type YEventHandler = (data: {
  value: any;
  formData: { [key: string]: any };
  ref: any;
  form: YForm;
  args: any[];
  config: YItemConfig;
  prop?: string;
}) => void;
export type YFnDirective = (form: YForm, config: YItemConfig) => YDirective;
export interface YEvent {
  name: string;
  handler?: YEventHandler;
  method?: string;
  native?: boolean;
  [key: string]: unknown;
}

export interface YDirective {
  name: string;
  value?: any;
  expression?: any;
  arg?: string;
  modifiers?: {
    [key: string]: boolean;
  };
}
export interface YFnParams {
  form: YForm;
  formData: { [key: string]: any };
  config: YItemConfig | YItemRow;
}
export type YFnBool = (options: YFnParams) => boolean;
export type YFnRule = (options: YFnParams) => YRule;
export type YFnRules = (options: YFnParams) => Array<YRule>;

export type YIf = { prop: string; value: any; isReverse?: boolean } | YFnBool;

export interface YFormItem {
  class?: { [key: string]: string } | string | string[] | YFnAny;
  style?: { [key: string]: string } | string | YFnAny;
  directives?: Array<YDirective | YFnDirective>;
  [key: string]: any;
}

export type YFormItemFn = (options: YFnParams) => YFormItem;

export type YFnStr = (options: YFnParams) => string;
export type YFnNum = (options: YFnParams) => number;
export type YFnObj = (options: YFnParams) => { [key: string]: any };
export type YFnAny = (options: YFnParams) => any;
export type YFnArray = (options: YFnParams) => any[];

export type YLifeHandler = (form: YForm, vm: any) => void;
export interface YItemConfigBase {
  label?: string | YFnStr;
  prop?: string | YFnStr;
  props?: string[] | YFnArray;
  bind?: { [key: string]: any } | YFnObj;
  required?: boolean | YFnBool;
  dictionaryName?: string | YFnStr;
  hide?: boolean | YFnBool;
  hideLabel?: boolean | YFnBool;
  notLabel?: boolean | YFnBool;
  value?: any | YFnAny;
  optionType?: string | YFnStr;
  errorMessage?: string | YFnStr;
  optionHandler?: (options: { [key: string]: string }[]) => Array<YItemConfig>;
  slotName?: string | YFnStr;
  ref?: string | YFnStr;
  notFormItem?: boolean | YFnBool;
  class?: { [key: string]: string } | string | string[] | YFnAny;
  style?: { [key: string]: string } | string | YFnAny;
  formItem?: YFormItem;
  nodes?: YNodes;
  slots?: { [key: string]: YNodes };
  col?: { [key: string]: any };
  events?: YEvent[];
  rules?: YRule[] | YFnRules;
  directives?: Array<YDirective | YFnDirective>;
  if?: YIf[] | YFnBool | boolean;
  show?: YIf[] | YFnBool | boolean;
  toFormData?: boolean | YFnBool;
  life?: { [key: string]: YLifeHandler };
  isHandlerBind?: boolean | string[];
  interception?: (value: any, lastValue: any) => any;
  [key: string]: any;
}

// itemConfig 数据结构
export interface YItemConfig extends YItemConfigBase {
  type: string | YFnStr;
}

// itemRow 数据结构
export interface YItemRow {
  id?: string;
  equally?: boolean;
  minSpan?: number;
  notFormItem?: boolean;
  bind?: { [key: string]: any };
  if?: YIf[] | YFnBool | boolean;
  show?: YIf[] | YFnBool | boolean;
  [key: string]: any;
}

export interface YRule {
  type?: string;
  required?: boolean;
  pattern?: string | object;
  min?: number;
  max?: number;
  len?: number;
  enum?: any[];
  whitespace?: string;
  transform?: Function;
  defaultField?: any[] | object;
  fields?: object;
  validator?: Function;
  asyncValidator?: Function;
  message?: string | Function;
  trigger?: string;
}

export interface YInterception {
  event?: Function;
  get?: (form: YForm, prop: string, value: any, config: YItemConfig) => any;
  set?: (form: YForm, prop: string, value: any, config: YItemConfig) => any;
  itemConfig?: {
    [key: string]: (config: YItemConfig, row?: YItemRow) => YItemConfig;
  };
  validate?: (form: any, callback: (isValid: boolean) => void) => void;
  props?: (
    props: { [key: string]: any },
    form: YForm
  ) => { [key: string]: any };
  rule?: (rule: YRule, form: YForm) => YRule;
  node?: (
    item: any,
    form: YForm
  ) => YItemConfig | Array<YItemConfig> | YItemRow;
}

export interface YDictionary {
  [key: string]: { label: string; value: any; [key: string]: any }[];
}

declare function getFormdataType(
  isValidate: boolean
): Promise<Record<string, any>>;
declare function getFormdataType(): Record<string, any>;

export interface YForm {
  baseComponentName: string;
  parentName?: string;
  name?: string;
  config: Array<
    YItemConfig | YItemRow | YItemRow[] | string | number | boolean
  >;
  scoped: { [key: string]: any };
  scopedData: { [key: string]: any };
  mounted?: Function;
  beforeCreate?: Function;
  created?: Function;
  beforeDestroy?: Function;
  rules: { [key: string]: YRule[] };
  layout: string;
  rightConfig: YItemRow[];
  localData: { [key: string]: any };
  formData: { [key: string]: any };
  identifier: string;
  newRules: { [key: string]: YRule[] };
  rowItemMap: ItemRowList;
  formUtil: FormUtil;
  createEntity: CreateEntity;
  $createElement?: Function;
  formParent?: any;
  dictionary: YDictionary;
  $scopedSlots?: { [key: string]: Function };
  interception: YInterception;
  firstNode: boolean;
  validate?: (
    isValid: boolean,
    params: { [key: string]: any },
    form: YForm
  ) => boolean;
  setDictionary: (dictionary: YDictionary) => YForm;
  getDictionary: () => YDictionary;
  getFormData: typeof getFormdataType;
  updateConfig: (isReset?: boolean) => YForm;
  updateFormData: (isReset?: boolean) => YForm;
  replaceConfig: (prop: string, config: YItemConfig) => YForm;
  setConfig: (prop: string, config: YItemConfig) => YForm;
  setRequired: (required: boolean, props?: string | string[]) => YForm;
  setDisabled: (disabled: boolean, props?: string | string[]) => YForm;
  setOptions: (prop: string, options: { [key: string]: any }[]) => YForm;
  getOptions: (prop: string) => { [key: string]: any }[] | undefined;
  setOptionsMap: (options: {
    [key: string]: { [key: string]: any }[];
  }) => YForm;
  setBind: (props: string | string[], key: string, value: any) => YForm;
  getBind: (prop: string, key?: string) => { [key: string]: any } | undefined;
  getConfigFileValue: (config: YItemConfig, key: string) => any;
  removeConfig: (prop: string | string[]) => YForm;
  push: (row: YItemRow) => YForm;
  refresh: () => YForm;
  getEvents: (prop: string) => YEvent[];
  getItemConfig: (prop?: string) => YItemConfig | undefined;
  getItemConfigs: () => YItemConfig[];
  appendRow: (config: YItemConfig | YItemRow | YItemRow[]) => YForm;
  appendRowBefore: (
    id: string | number,
    config: YItemConfig | YItemRow | YItemRow[]
  ) => YForm;
  appendRowAfter: (
    id: string | number,
    config: YItemConfig | YItemRow | YItemRow[]
  ) => YForm;
  getRow: (id: string | number) => YItemRow | undefined;
  getRows: () => YItemRow[];
  trigger: (
    prop: string,
    eventName: string,
    value?: any,
    callBack?: Function
  ) => void;
  getFields: () => { [key: string]: any };
  setFormData: (
    key: string | { [key: string]: any },
    value?: any,
    toLocal?: boolean
  ) => YForm;
  setFormDataMap: (data: { [key: string]: any }, toLocal?: boolean) => YForm;
  removeRow: (index: string | number) => YForm;
  appendConfigBefore: (prop: string, config: YItemConfig) => YForm;
  appendConfigAfter: (prop: string, config: YItemConfig) => YForm;
  clearValidate: (props?: string[]) => YForm;
  queryLikeItemConfig: (key: string, value: string | number) => YItemConfig[];
  queryLikeRow: (id: string | number) => YItemRow[];
  setGlobal: (key: string, value: any) => YForm;
  setGlobalMap: (data: { [key: string]: any }) => YForm;
  getGlobal: (key?: string) => any;
  setScopedData: (key: string, value: any) => YForm;
  setScopedDataMap: (data: { [key: string]: any }) => YForm;
  getScopedData: (key?: string) => any;
  getAllForm: () => { [key: string]: any };
  getFormByName: (name: string) => any | undefined;
  setComponents: (components: { [key: string]: any }) => YForm;
  getComponents: () => { [key: string]: any };
  getParentMethod: (eventName: string) => Function | undefined;
  triggerParent: (eventName: string, ...args: any) => YForm;
  getRef: (refName: string) => any;
  getRefs: () => { [key: string]: any };
  getOriginRef: () => any;
  triggerRef: (refName: string, eventName: string, ...args: any) => YForm;
  getParent: () => any | undefined;
  removeFormData: (key: string) => YForm;
  isPromise: (fn: any) => boolean;
}

export type FormMapping = {
  model?: string;
  prop?: string;
  linkPath?: string;
};

export interface YMapping {
  UIFormMapping?: { form: string; props: string[] };
  componentMapping?: { [key: string]: any };
  emitMapping?: { [key: string]: string[] };
  placeholderMapping?: { [key: string]: (label: string) => string | string[] };
  modelMapping?: { [key: string]: string };
  defaultValueMapping?: { [key: string]: any };
  defaultRequiredRuleMapping?: {
    [key: string]: (config: YItemConfig, message: string | string[]) => YRule[];
  };
  formMapping?: FormMapping;
}
